﻿using System;
using System.IO;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;


namespace Netfraction.Network.Protocol.ADCS.Security
{
    public class Certificate
    {
        public void GenerateCertificates()
        {
            string userCID = "SU6CG5D6PTRSNWBCB3HRLCJIAABSDD3TKZLTKMA";
            string certPath = @"C:\Certificates\";

            // Steps
            // 1. Generate the RSA Key Pair.
            // 2. Set the X.509 certificate members.
            // 3. Generate the X.509 certificate.
            // 4. Write the X.509 certificate to file.
            // 5. Write the RSA Private Key to file.

            X509Certificate x509;
            const string CERT_FILE_NAME = "my.crt";
            const string KEY_FILE_NAME = "my.key";
            const int CERTIFICATE_LIFESPAN_DAYS = 10; 
            const int KEY_LENGTH = 2048;
            
            // 1. Generate the RSA Key Pair.
            SecureRandom secureRandom = new SecureRandom();
            BigInteger publicExponent = BigInteger.ValueOf(0x10001L); // 0x10001 = RSA_F4 from OpenSSL's rsa.h

            RsaKeyGenerationParameters rsaKeyGenParams = new RsaKeyGenerationParameters(publicExponent, secureRandom, KEY_LENGTH, 80);   // Not sure what certainty = 80 means :S
            RsaKeyPairGenerator rsaKeyPairGen = new RsaKeyPairGenerator();
            rsaKeyPairGen.Init(rsaKeyGenParams);

            AsymmetricCipherKeyPair keyPair = rsaKeyPairGen.GenerateKeyPair();

            // 2. Set the X.509 certificate members.
            X509V1CertificateGenerator xGen = new X509V1CertificateGenerator();
            BigInteger serialNum = BigInteger.One; // Should generate something random here, at least 64-bits e.g. DateTime.Now.Ticks or System.Random
            X509Name xName = new X509Name(string.Format("CN={0}", userCID)); // Set the name to the user's client Id

            xGen.SetSerialNumber(serialNum);
            xGen.SetIssuerDN(xName);
            xGen.SetNotBefore(DateTime.Now);
            xGen.SetNotAfter(DateTime.Now.AddDays(CERTIFICATE_LIFESPAN_DAYS));
            xGen.SetSubjectDN(xName);

            xGen.SetPublicKey(keyPair.Public);
            xGen.SetSignatureAlgorithm("SHA1withRSA");

            // 3. Generate the X.509 certificate.
            x509 = xGen.Generate(keyPair.Private);

            // 4. Write the X.509 certificate to file.
            StreamWriter sWriter;
            PemWriter pWriter;

            sWriter = new StreamWriter(new FileStream(Path.Combine(certPath, CERT_FILE_NAME), FileMode.Create));
            pWriter = new PemWriter(sWriter);
            pWriter.WriteObject(x509);
            sWriter.Flush();
            sWriter.Close();

            // 5. Write the RSA Private Key to file.
            sWriter = new StreamWriter(new FileStream(Path.Combine(certPath, KEY_FILE_NAME), FileMode.Create));
            pWriter = new PemWriter(sWriter);
            pWriter.WriteObject(keyPair);
            sWriter.Flush();
            sWriter.Close();
        }
    }
}
